/**
 * 常规工具包，全名为CommonUtils，简写为cu。
 * 这里的方法与应用无关，大概包括以下内容：
 * · 异步请求工具，如get, post, submit等。
 * · 消息提示工具，如alert, confirm, hint等。
 * · 同步加载模块文件并创建Ext类的实例。
 * · 为调试方便添加全局方法log，相当为console.log或window.alert。
 * 注意：加载此文件前必须先加载LangUtils.js
 */
define(function(){
	window.cu = window.CommonUitls = {
		/**
		 * 异步使用GET方式获取数据，默认提示等待消息。
		 * @param options 参看request方法
		 */
		get: function(options){
			//提示信息
			if (lu.isUndefined(options.waitMsg)) options.waitMsg = 'load';
			options.method = 'GET';
			//提交请求
			return this.request(options);
		},
		/**
		 * 异步使用POST方式提交数据，默认提示等待消息。
		 * @param options 参看request方法
		 */
		post: function(options){
			//提示信息
			if (lu.isUndefined(options.waitMsg)) options.waitMsg = 'submit';
			options.method = 'POST';
			//提交请求
			return this.request(options);
		},
		/**
		 * 异步提交表单
		 * @param options 简单说明如下（详细请参看request方法）：
		 * ·form Ext.form.BasicForm
		 * ·url 表单的请求地址
		 * ·valid 是否需要验证表单，默认为需要
		 * ·before 表示验证通过之后执行的方法，如果返回false则不提交表单
		 * ·method 提交方法，默认为'POST'
		 * ·isUpload 是否要上传文件
		 */
		submit: function(options){
			if (!options || !options.form){
				alert('CommonUtils.submit(options): 参数有误，表单(options.form)不能为空');
				return;
			}
			//表单验证
			if (options.valid !== false && !options.form.isValid()){
				this.hint('<nobr>表单验证不通过</nobr>', 800);
				return;
			}
			//执行提交之前的检查，
			if (options.before && options.before.call(options.scope, options.form) === false){
				return;
			}
			options.form = options.form.el;
			options.waitMsg = 'submit';
			if (!options.method) options.method = 'POST';
			//提交表单
			return this.request(options);
		},
		/**
		 * 封装Ext.Ajax.request方法，添加处理全局异常。
		 * @param options 基本与Ext.Ajax.request相同，做了如下修改：
		 * · success选项： 请求成功后执行的方法，只有一个参数，即请求返回的结果。
		 * · failure选项： 请求出错后执行的方法，只有一个参数，即请求出错的消息。
		 * · 添加waitMsg选项，表示等待请求时提示消息，'load'表示调用用cu.waitLoading()，
		 *    'submit'表示调用cu.waitSubmitting()，其余表示调用cu.wait(waitMsg)，为空表示不做任何提示。
		 **/
		request: function(options){
			//覆盖callback方法，让Ext.Ajax.request请求后只执行此方法。
			var successFn = options.success,
				failureFn = options.failure,
				callbackFn = options.callback;
			options.hasFailureFn = failureFn ? true : false; //标记是否有出错处理方法，在ExceptionHandler.js中处理
			options.success = null; //取消原ext的方法
			options.failure = null; //取消原ext的方法
			options.callback = function(opts, success, response){
				if (ExceptionHandler.fetchError(opts.ajaxNum)){ 
					//请求出错的情况，如果有出错处理方法则执行它。（异常错误在ExceptionHandler.js中标记）
					if (lu.isFn(failureFn)){
						failureFn.call(options.scope, lu.parseJson(response.responseText).errorMsg);
					}
				}
				else{
					//请求成功返回的情况，如果有成功处理方法则执行它。先假设返回结果是json，如果转换出错再当是text来处理。
					if (lu.isFn(successFn)){
						result = response.responseText ? response.responseText.trim() : '';
						try{
							if (result.match(/^\{.*\}$/) || result.match(/^\[.*\]$/)){ //匹配json
								result = lu.parseJson(result);
							}
						}
						catch(e){
							
						}
						successFn.call(options.scope, result);
					}
				}
				//不管失败还是成功都执行callback方法
				if (lu.isFn(callbackFn)) callbackFn.call(options.scope, response);
			};
			//处理等待提示消息
			if (options.waitMsg === 'load'){
				this.waitLoading();
			}
			else if (options.waitMsg === 'submit'){
				this.waitSubmitting();
			}
			else if (options.waitMsg){
				this.wait(options.waitMsg);
			}
			//调用Ext的方法来发送异步请求
			return Ext.Ajax.request(options);
		},
		/**
		 * 发起同步请求，返回执行结果。结果会自动转换为json，如果不是json则当字符串处理。
		 * @param method 默认为get，可选值为get,post
		 */
		sync: function(url, method){
			if (method != 'get' && method != 'post') method = 'get';
			var http = null;
			try{
				http = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
			}
			catch(e){
				alert('当前浏览器不支持AJAX');
				return null;
			}
			http.open(method, encodeURI(url), false);
			http.send();
			if (!ExceptionHandler.handleError(null, http, null)){ //检查异常
				return lu.parseJson(http.responseText);
			}
			return null;
		},
		/**
		 * 获取适合的窗口宽度，不允许窗口的宽度比body的宽度大
		 * @param width 指定的宽度，如果此参数为空则返回最大允许宽度
		 * @return 适合范围的宽度
		 */
		getWinWidth: function(width){
			var bodyWidth = Ext.getBody().getViewSize().width;
			if (!width || width >= bodyWidth){
				return bodyWidth - 20;
			}
			return width;
		},
		/**
		 * 获取适合的窗口高度，不允许窗口的高度比body的高度大
		 * @param height 指定的高度，如果此参数为空则返回最大允许高度
		 * @return 适合范围的高度
		 */
		getWinHeight: function(height){
			var bodyHeight = Ext.getBody().getViewSize().height;
			if (!height || height >= bodyHeight){
				return bodyHeight - 20;
			}
			return height;
		},
		/**
		 * 弹出提示等待的信息窗口
		 */
		wait: function(msg){
			this.loadMask = new Ext.LoadMask(Ext.getBody(), {msg: msg});
			this.loadMask.show();
		},
		stopWaiting: function(){
			if (this.loadMask){
				this.loadMask.hide();
				this.loadMask = null;
			}
		},
		/**
		 * 弹出提示等待数据加载的信息窗口
		 */
		waitLoading: function(){
			this.wait('加载数据中……');
		},
		/**
		 * 弹出提示正在提交请求的信息窗口
		 */
		waitSubmitting: function(){
			this.wait('提交请求中……');
		},
		/**
		 * 弹出提示窗口，并在一段时间内自动关闭。
		 * 如果timeout为空，则根据信息长度计算时间，最小为800毫秒.
		 * @param msg 要提示的信息
		 * @param timeout 提示信息显示的时间长度，单位为毫秒，如果此参数为空，则根据信息长度计算时间，最小为800毫秒。
		 * @param callback 提示信息后的回调方法
		 * @param scope 回调方法的作用域
		 */
		hint: function(msg, timeout, callback, scope){
			if (lu.isFn(timeout)){
				scope = callback; callback = timeout; timeout = null;
			}
			timeout = timeout || (msg.length > 5 ? msg.length * 150 : 800);
			if (!msg.match(/<[\w|\/]+>/)) msg = '<nobr>' + msg + '</nobr>'; //对普通文本不断行
		    Ext.Msg.show({
		        title: '提示',msg: msg, modal: false, icon: Ext.Msg.WARNING
		    });
		    setTimeout(function(){
		    	Ext.Msg.hide();
		    	callback && callback.call(scope);
		    }, timeout);
		},
		//TODO: 有待借用的一个显示消息的方法
		slipMsg: function(title, format){
            if(!this.msgCt){
            	var style= 'position:absolute;left:35%;top:10px;width:250px; z-index:20000;';
                this.msgCt = Ext.DomHelper.insertFirst(document.body, {id:'msg-div', style:style}, true);
            }
            this.msgCt.alignTo(document, 't-t');
            var s = String.format.apply(String, Array.prototype.slice.call(arguments, 1));
            var m = Ext.DomHelper.append(this.msgCt, {html:createBox(title, s)}, true);
            m.slideIn('t').pause(5).ghost("t", {remove:true});
            function createBox(t, s){
            	 return ['<div class="msg">',
                         '<div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>',
                         '<div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc"><h3>', t, '</h3>', s, '</div></div></div>',
                         '<div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>',
                         '</div>'].join('');
            }
		},
		/**
		 * 弹出信息窗口
		 * @param title 信息窗口标题
		 * @param msg 信息内容
		 * @param icon 窗口icon类型：'i'-信息窗口；'w'-警告窗口；'e'-错误窗口
		 * @param callback 关闭窗口后的回调方法
		 */
		showMsg: function(title, msg, icon, callback){
			if (icon == 1 || icon == 'i') icon = Ext.MessageBox.INFO;
			else if (icon == 2 || icon == 'w') icon = Ext.MessageBox.WARNING;
			else if (icon == 3 || icon == 'e') icon = Ext.MessageBox.ERROR;
			else icon = Ext.MessageBox.INFO;
			if (!msg.match(/<[\w|\/]+>/)) msg = '<nobr>' + msg + '</nobr>'; //对普通文本不断行
			Ext.MessageBox.show({
				title: title, msg: msg, buttons: Ext.MessageBox.OK, fn: callback, icon: icon
		    });
		},
		/**
		 * 弹出普通信息窗口
		 * @param msg 信息内容
		 * @param cb 窗口关闭后的回调方法
		 * @param scope 回调方法的this指向
		 */
		alert: function(msg, cb, scope){
			var callback = cb ? function(){cb.call(scope);} : null;
			this.showMsg('提示', msg, 'w', callback);
		},
		/**
		 * 弹出提问窗口
		 * @param msg 提问信息的内容
		 * @param yesFn 点击“是”后的回调方法
		 * @param noFn 点击“否”后的回调方法
		 * @param scope 回调方法的作用域
		 */
		confirm: function(msg, yesFn, noFn, scope){
			if (!lu.isFn(noFn)){
				scope = noFn;
				noFn = null;
			}
			scope = scope || this;
			if (!msg.match(/<[\w|\/]+>/)) msg = '<nobr>' + msg + '</nobr>'; //对普通文本不断行
			Ext.Msg.confirm('提示', '<br/>' + msg + '<br/>', function(btn){
				if (btn === 'yes' && lu.isFn(yesFn)){
					yesFn.call(scope);
				}
				else if (btn === 'no' && lu.isFn(noFn)){
					noFn.call(scope);
				}
			});
		},
		/**
         * 弹出一个提示框来接受用户输入。
         * @param msg 对输入值的描述，起提示作用
         * @param okFn 用户点击“确定”按钮，并且输入值合法时执行的回调方法
         * @param validator 检验输入值是否合法的方法，参数为scope和输入值，只有返回true时输入值才合法，其它情况应该返回错误消息以进一步提示用户。
         *                  只要这个校验方法不返回true，提示框就不断地显示出来，直到校验正确或者用户取消才结束。
         * @param scope 回调方法的作用域
         * @param multiline 为true或者数字时表示输入框是多行文本框，为false时表示输入框是单行文本框，默认为false
         * @param defaultValue 在输入框中显示的默认值
         */
        prompt: function(msg, okFn, validator, scope, multiline, defaultValue){
            if (!lu.isFn(validator)){
                defaultValue = multiline; multiline = scope; scope = validator; validator = null;
            }
            var prompt = function(msg2){
                Ext.Msg.prompt('提示', msg2, function(btn, value){
                    if (btn === 'cancel') return;
                    if (validator){ //检查输入值的合法性，如果不合法则继续等待输入
                        var r  = validator.call(scope, value);
                        if (r !== true){
                            defaultValue = value;
                            prompt(msg + '<br/>(<font color="red">' + r + '</font>)');
                            return;
                        }
                    }
                    okFn.call(scope, value);
                }, scope, multiline, defaultValue);
            };
            prompt(msg);
        },
		/**
		 * 给指定的组件添加tooltip提示
		 * @param comp Ext组件对象
		 * @param tips 要提示的内容，可以是html
		 */
		tips: function(comp, tips){
			var tooltip = new Ext.ToolTip({
				target: comp.getEl(),
				padding: '3',
				dismissDelay: 0, //鼠标移开组件后才消失
				html: tips
			});
			comp.on('destroy', function(){
				tooltip.destroy();
			});
		},
		/**
		 * 在列表中删除记录的通用方法，options：
		 * · grid 要在其中删除记录的列表控件
		 * · recordName 记录的名字，用于提示
		 * · handler 具体的删除操作，参数要选中的记录集(records)
		 * · scope handler方法的this指向，默认是grid
		 */
		removeInGrid: function(options){
			var records = options.grid.getSelectionModel().getSelections();
			var recordName = options.recordName || '';
			if (records.length == 0){
				cu.hint('请选择要删除的' + recordName);
				return;
			}
			var msg = '确定要删除选中的{0}条{1}记录吗？'.f(records.length, recordName);
			cu.confirm(msg, function(){
				options.handler.call(options.scope || options.grid, records);
			});
		},
		/**
		 * 获取默认的窗口的动态效果目标
		 */
		getDefaultAnimateTarget: function(){
			return Ext.getBody();
		},
		/**
		 * 用同步加载方法创建js模块对象
		 */
		create: function(jsModule, cfg){
			var Class = use2(jsModule);
			if (!Class){
				this.showMsg('错误提示', '加载模块“' + jsModule + '”出错，请检查', 'e');
				return {};
			}
			if (!lu.isFn(Class)){
				this.showMsg('错误提示', '模块“' + jsModule + '”没有正确返回，请检查', 'e');
				return {};
			}
			return new Class(cfg);
		},
		/**
		 * 给元素或者元素的子元素绑定回车事件，要求子元素具有name或hiddenName属性。
		 * 通过用来给FormPanel的元素绑定回车事件。
		 * @param comp 要绑定事件的元素，可能是面板或元素
		 * @param fn 回车事件执行的方法
		 * @param scope fn的this指向
		 */
		bindEnter: function(comp, fn, scope){
			if (!lu.isFn(fn)){
				debug('错误：cu.bindEnter(comp,fn,scope)方法缺少参数：fn-回车事件要执行的方法');
				return;
			}
			var _bind = function(c){
				if (c.items){ //有子元素，递归绑定子元素
					c.items.each(function(item){_bind(item);});
				}
				else{
					if (!c.name && !c.hiddenName) return;
					c.on('specialKey', function(field, e){
		        		if (e.getKey() == e.ENTER) fn.call(scope || comp);
		        	});
				}
			}
			_bind(comp);
		},
		/**
		 * 给组件绑定回车事件，并自动销毁
		 * @param comp 要绑定回车事件的Ext组件，例如窗体
		 * @param fn 代表回车事件的方法
		 * @param scope fn的this指向，默认为comp
		 */
		bindEnter2: function(comp, fn, scope){
			comp.enterKeyNav = new Ext.KeyNav(comp.el, {enter: function(){fn.call(scope || comp);}});
			comp.on('destroy', function(){
				this.enterKeyNav.disable();
				delete this.enterKeyNav;
			});
		},
		/**
		 * 创建表格的分页条
		 * @param gridStore 表格的数据存储对象
		 * @param defaultPageSize 默认每页显示记录数，默认为25
		 */
		createPagingBar: function(gridStore, defaultPageSize){
			/**
			 * 注意每页记录数的参数名，store的默认参数为'limit'，PagingToolbar的为'pageSize'，两者要小心处理
			 */
			if (!gridStore){
				alert('createPagingBar()方法必须传入store参数');return;
			}
			//设置初始每页记录数
			defaultPageSize = defaultPageSize || 25;
			if (!gridStore.baseParams) gridStore.baseParams = {};
			gridStore.baseParams.limit = defaultPageSize;
			return new Ext.PagingToolbar({
				pageSize: defaultPageSize, //注意Ext.ux.ProgressBarPager用到这个参数名
			    store: gridStore,
			    displayInfo: true,
			    displayMsg: '共 {2} 条记录，当前显示第 {0} - {1} 条',
			    emptyMsg: "没有找到记录",
			    items:['-', {
			    	xtype: 'combo',
					mode: 'local',
					triggerAction: 'all',
					store: [[25, '25 条/页'], [50, '50 条/页'], [100, '100 条/页'], 
					        [200, '200 条/页'], [300, '300 条/页'], [500, '500 条/页']],
					editable: false,
					value: defaultPageSize,
					width: 80,
					style: 'margin-left:10px;',
					listeners:{
						select: function(combo, record, index){
							//注意要通过gridStore.baseParams来设置分页相关的参数
							//设置store的参数，用于提交请求
							gridStore.baseParams.limit = this.value;
							gridStore.baseParams.start = 0;
							//设置分页条的参数，用于显示和计算
							this.ownerCt.pageSize = this.value;
							gridStore.reload();
						}
					}
			    }],
			    plugins: new Ext.ux.ProgressBarPager()
			});
		},
		/**
		 * 获取icon图标路径
		 */
		getIconImg: function(img){
			return 'theme/default/images/icon/' + img;
		},
		//private: 标记当前的异步请求是否有错
		ajaxError: {},
		//private: 标记当前异步请求出错后的回调方法
		ajaxErrorFn: {},
		//private: 提示等待信息的对象
		loadMask: null,
		//private: 通过ajax方法来发生请求
		ajaxNum: 0,
		none: null //占位
	};
});